package com.saturn.titan.core.rest;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestTemplate;
import org.springside.modules.mapper.JsonMapper;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;

/**
 * 提供 rest 服务
 */
@Scope("prototype")
@Component
public class RestService {

	private static Logger logger = LoggerFactory.getLogger(RestService.class);

	private String serverPath;

	private static class FileDownloadResponseExtractor implements ResponseExtractor<Boolean> {
		
		private final File file;

		private File file() {
			return this.file;
		}

		private FileDownloadResponseExtractor(String file) {
			this.file = new File(file);
		}

		public Boolean extractData(ClientHttpResponse response) throws IOException {
			try {
				InputStream is = response.getBody();
				OutputStream os = new BufferedOutputStream(new FileOutputStream(file()));

				IOUtils.copyLarge(is, os);
				IOUtils.closeQuietly(is);
				IOUtils.closeQuietly(os);
				return true;
			} catch (Exception e) {
				return false;
			}
		}
	}

	private final JsonMapper mapper = new JsonMapper();

	private RestTemplate httpClientRestTemplate;

	private HttpComponentsClientHttpRequestFactory httpClientRequestFactory;

	public RestService() {
		httpClientRestTemplate = new RestTemplate();
		httpClientRequestFactory = new HttpComponentsClientHttpRequestFactory();

		httpClientRequestFactory.setConnectTimeout(300000);
		httpClientRequestFactory.setReadTimeout(300000);
		httpClientRestTemplate.setRequestFactory(httpClientRequestFactory);
	}

	/**
	 * 返回类型为复杂类型的调用
	 * 
	 * @param method
	 *            调用的方法
	 * @param responseType
	 *            返回类型 ex: new ResponseTypeListBean(){}
	 * @param param
	 *            参数
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T call(RestMethod method, TypeReference<T> responseType, Object param) {
		String response = call(method, String.class, param);
		try {
			return (T)mapper.getMapper().readValue(response, responseType);
		} catch (JsonParseException jpe) {
			logger.error("转换 response 异常", jpe);
		} catch (JsonMappingException jme) {
			logger.error("转换 response 异常", jme);
		} catch (IOException ie) {
			logger.error("转换 response 异常", ie);
		}
		return null;
	}

	/**
	 * 针对文件下载接口的特殊调用
	 * 
	 * @param method 方法名
	 * @param param 参数[0]接口所需参数下载文件名[1]
	 * @return
	 */
	public Boolean callDownLoadFileRequest(RestMethod method, Object[] param) {
		if (param.length < 2) {
			logger.error("调用下载文件接口所传参数缺失");
			return null;
		}
		Boolean response = httpClientRestTemplate.execute(serverPath + method.getMethodName() + processParam(param[0]), HttpMethod.GET, null,
				new FileDownloadResponseExtractor(param[1].toString()));
		return response;
	}

	public <T> T call(RestMethod method, Class<T> responseType, Object param) {
		if (method.getMethodType().equals(RestMethod.HTTP_GET)) {
			return httpClientRestTemplate.getForObject(serverPath + method.getMethodName() + processParam(param), responseType);
		} else {
			ResponseEntity<T> response = httpClientRestTemplate.postForEntity(serverPath + method.getMethodName(), param, responseType);
			return response.getBody();
		}
	}

	public String call(RestMethod method, Object param) {
		return call(method, String.class, param);
	}

	public String call(RestMethod method) {
		Object[] p = null;
		return call(method, String.class, p);
	}

	public <T> T call(RestMethod method, Class<T> responseType) {
		Object[] p = null;
		T obj = call(method, responseType, p);
		return obj;
	}

	public String getServerPath() {
		return serverPath;
	}

	public void setServerPath(String serverPath) {
		this.serverPath = serverPath;
	}

	private String processParam(Object param) {
		String paramStr = "";
		if (param != null) {
			paramStr = "/" + param;
		}
		return paramStr;
	}

}
